package com.cloud.tool.aop;

import com.alibaba.fastjson.JSON;
import com.cloud.tool.annotation.IdempotencyCheck;
import com.cloud.tool.exception.ToolException;
import com.cloud.tool.service.LuaTool;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author WangZY
 * @Date 2022/2/21 17:21
 * @Description 基于漏斗思想的限流器
 **/
@Aspect
@Component
@Slf4j
@ConditionalOnProperty(name = "tool.redis-enable", havingValue = "true", matchIfMissing = true)
public class IdempotencyCheckHandler {

    @Autowired
    private LuaTool luaTool;
    @Autowired
    private ConfigurableEnvironment config;

    @Pointcut("@annotation(com.ruijie.tool.annotation.IdempotencyCheck)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Object[] objects = joinPoint.getArgs();
        IdempotencyCheck check = methodSignature.getMethod().getAnnotation(IdempotencyCheck.class);
        int checkTime = check.checkTime();
        String checkKey = check.checkKey();
        String application = config.getProperty("spring.application.name");
        String methodName = methodSignature.getName();
        String key = "";
        if (StringUtils.isEmpty(application)) {
            throw new ToolException("当前项目必须拥有spring.application.name才能使用限流器");
        } else {
            key = application + ":" + methodName + ":";
        }
        if (StringUtils.isEmpty(checkKey)) {
            // userId从你的SSO中获取
            String userId = "选择你的英雄";
            String digest = DigestUtils.md5DigestAsHex(JSON.toJSONBytes(getRequestParams(joinPoint)));
            key = key + userId + ":" + digest;
        } else {
            key = key + checkKey;
        }
        long checkRes = luaTool.idempotencyCheck(key, checkTime);
        if (checkRes == -1) {
            log.info("幂等性校验已开启，当前Key为{}", key);
        } else {
            throw new ToolException("防重校验已开启，当前方法禁止在" + checkTime + "秒内重复提交");
        }
        return joinPoint.proceed(objects);
    }

    /***
     * @Author WangZY
     * @Date 2020/4/16 18:56
     * @Description 获取入参
     */
    private String getRequestParams(ProceedingJoinPoint proceedingJoinPoint) {
        Map<String, Object> requestParams = new HashMap<>(16);
        //参数名
        String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];
            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                //获取文件名
                value = file.getOriginalFilename();
                requestParams.put(paramNames[i], value);
            } else if (value instanceof HttpServletRequest) {
                requestParams.put(paramNames[i], "参数类型为HttpServletRequest");
            } else if (value instanceof HttpServletResponse) {
                requestParams.put(paramNames[i], "参数类型为HttpServletResponse");
            } else {
                requestParams.put(paramNames[i], value);
            }
        }
        return JSON.toJSONString(requestParams);
    }
}
